//------------------------------------------------------------------------------
//
// classname: ServiceManager
// version: 0.1.0
// author: 小兵( aosnow@yeah.net )
// created: 2013-3-22
// copyright (c) 2013 小兵( aosnow@yeah.net )
// HTTP服务请求功能类
//
//------------------------------------------------------------------------------

package starfire.managers
{
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.utils.Dictionary;

	import starfire.events.ServiceEvent;
	import starfire.namespaces.sn_internal;
	import starfire.rpc.http.HttpRequestMessage;
	import starfire.rpc.http.HttpService;

	use namespace sn_internal;

	public class ServiceManager
	{

		//--------------------------------------------------------------------------
		//
		//  Class variables
		//
		//--------------------------------------------------------------------------

		private var _serviceCache:Dictionary;
		private var _numService:int;

		//--------------------------------------------------------------------------
		//
		//   Class constructor
		//
		//--------------------------------------------------------------------------

		private static var _instance:ServiceManager;

		public function ServiceManager()
		{
			if( _instance != null )
				throw new Error( "请通过 ServiceManager.getInstance() 调用唯一实例！" );

			_serviceCache = new Dictionary();
			_numService = 0;
		}

		public static function getInstance():ServiceManager
		{
			if( _instance == null )
				_instance = new ServiceManager;

			return _instance;
		}

		public function destroy():void
		{
			var httpServices:Vector.<HttpService>;
			var length:int;

			for( var propName:String in _serviceCache )
			{
				httpServices = _serviceCache[ propName ] as Vector.<HttpService>;
				length = httpServices.length;

				for( var i:int = 0; i < length; i++ )
				{
					httpServices[ i ].destroy();
					delete httpServices[ i ];
				}

				delete _serviceCache[ propName ];
			}

			_numService = 0;
			_serviceCache = null;
		}

		//--------------------------------------------------------------------------
		//
		//   Class methods
		//
		//--------------------------------------------------------------------------

		/**
		 * 发送HTTP服务请求
		 * @param message					- 发送请求的相关配置
		 * @param complete					- 请求完成时的回调
		 * @param error						- 请求操作发生错误时的回调
		 * @param progress					- 请求进度进行时的回调
		 * @param timeOut					- 请求超时终止时的回调
		 *
		 * <p>
		 * <b>complete</b> 示例：<br><code>function complete( event:ServiceEvent ):void{}
		 * </code></p>
		 * <p>
		 * <b>error</b> 示例：<br>
		 * event 参数的类型可能是以下3种事件类型中的任何一种：<br>
		 * HTTPStatusEvent、SecurityErrorEvent、IOErrorEvent<br><br>
		 * <pre>
		 *function error( event:Event ):void{
		 *	if(event is HTTPStatusEvent)
		 *	{
		 *		trace("HTTPStatusEvent 状态", ( event as HTTPStatusEvent ).status);
		 *	}
		 *	else if(event is SecurityErrorEvent)
		 *	{
		 *		trace("服务请求发生安全错误");
		 *	}
		 *	else if(event is IOErrorEvent)
		 *	{
		 *		trace("服务请求发生 IO 错误");
		 *	}
		 *}
		 * </pre></p>
		 * <p><b>progress</b> 示例：<br><code>
		 * info为加载进度数据数组：[ bytesLoaded:Number, bytesTotal:Number ]<br>
		 * function progress( info:Array ):void{}
		 * </code></p>
		 * <p><b>timeOut</b> 示例：<br><code>function timeOut():void{}
		 * </code></p>
		 */
		public function send( message:HttpRequestMessage, // 发送数据的配置
							  complete:Function = null, // 完成回调
							  error:Function = null, // 错误回调
							  progress:Function = null, // 进度回调
							  timeOut:Function = null // 超时回调
							  ):void
		{
			var service:HttpService = HttpService.fromPool( message.command, message );

			service.dataRequest = message.data;
			service.setCallback( complete, "complete" );
			service.setCallback( error is Function ? error : errorHandler, "error" );
			service.setCallback( progress, "progress" );
			service.setCallback( timeOut, "timeOut" );

			addAndSendService( service );
		}

		/** 默认的HTTP请求错误处理  **/
		protected function errorHandler( event:Event = null ):void
		{
			switch( event.type )
			{
				case SecurityErrorEvent.SECURITY_ERROR: // 安全错误
				{
					var securityEvent:SecurityErrorEvent = event as SecurityErrorEvent;
					trace( "安全错误：无法访问。" );
					break;
				}
				case IOErrorEvent.IO_ERROR: // 错误导致了输入或输出失败
				{
					var ioEvent:IOErrorEvent = event as IOErrorEvent;
					trace( ioEvent.text );
					break;
				}
				case HTTPStatusEvent.HTTP_STATUS: // 除 200 以外的其它状态错误
				{
					var httpEvent:HTTPStatusEvent = event as HTTPStatusEvent;
					trace( "服务器错误： status id - ", httpEvent.status );
					break;
				}
				default:
				{
					trace( "未知错误。" );
					break;
				}
			}
		}

		/**
		 * 新添加一个HTTP服务请求
		 *
		 * <p>
		 * 若添加的服务名相同，则可能的处理结果分为以下几种:
		 * <li><b>服务名、提交数据、complete回调方法都相同</b>：这种请求将被忽略，因为相同功能的服务已提交</li>
		 * <li><b>服务名、提交数据相同，complete回调方法不同</b>：以最后请求为准发出一次请求，返回数据时通知每个回调方法</li>
		 * <li><b>服务名相同，提交数据不同</b>：这种情况不会进行整合，而是看成两个不同服务，按先后顺序一一发送请求，
		 * 待数据返回时通知回调方法</li>
		 * </p>
		 * @param newService			- 服务请求对象
		 */
		public function addAndSendService( newService:HttpService ):void
		{
			var submit:HttpService;
			var httpServices:Vector.<HttpService> = hasService( newService.name );

			if( httpServices == null || httpServices.length <= 0 )
			{
				submit = newService;

				if( httpServices == null )
				{
					_serviceCache[ newService.name ] = new Vector.<HttpService>;
					_numService++;
				}

				_serviceCache[ newService.name ].push( newService );
			}
			else
			{
				var s:HttpService;
				var length:int = httpServices.length;
				var sameDataService:HttpService;

				// 尝试搜索数据相同的服务
				for( var i:int = 0; i < length; i++ )
				{
					s = httpServices[ i ];

					if( s.dataRequest == newService.dataRequest )
					{
						sameDataService = s;
						break;
					}
				}

				// 若有数据相同的服务，则进行整合回调方法或者直接忽略
				if( sameDataService != null )
				{
					if( isExistingMethod( sameDataService, newService, "complete" ) //
					&& isExistingMethod( sameDataService, newService, "error" ) //
					&& isExistingMethod( sameDataService, newService, "progress" ) //
					&& isExistingMethod( sameDataService, newService, "timeOut" ))
					{
						// 忽略请求
						pushServicePool( newService );
						return;
					}
					else
					{
						// 只须整合回调方法即可，服务会在返回时自动去调用整合后的所有回调方法
						integrateServiceHandler( sameDataService, newService, "complete" );
						integrateServiceHandler( sameDataService, newService, "error" );
						integrateServiceHandler( sameDataService, newService, "progress" );
						integrateServiceHandler( sameDataService, newService, "timeOut" );
						return;
					}
				}
				else
				{
					httpServices.push( newService );
					submit = newService;
				}
			}

			// 监听完成事件，完成后自动释放资源
			submit.addEventListener( ServiceEvent.COMPLETE, completeHandler );

			// 进行新的服务请求
			submit.send();
		}

		/**
		 * 搜索服务管理器缓存中是否已经存在此服务类型
		 * @param name					- 服务名
		 * @return 返回相同服务名的服务数组 Vector.&lt;HttpService&gt;
		 */
		public function hasService( name:String ):Vector.<HttpService>
		{
			if( _serviceCache && _numService > 0 )
			{
				if( _serviceCache.hasOwnProperty( name ))
					return _serviceCache[ name ];
			}

			return null;
		}

		/** 整合回调方法，将 newServie 中的方法都整合到 service 中  **/
		private function integrateServiceHandler( service:HttpService, newService:HttpService, handlerName:String ):void
		{
			var funcs:Vector.<Function> = newService[ handlerName ] as Vector.<Function>;

			if( funcs.length )
			{
				while( funcs.length )
				{
					service.setCallback( funcs.pop(), handlerName );
				}
			}
		}

		private function completeHandler( event:ServiceEvent ):void
		{
			var service:HttpService = event.target as HttpService;
			var httpServices:Vector.<HttpService> = hasService( service.name );

			if( httpServices && httpServices.length > 0 )
			{
				var length:int = httpServices.length;
				var curService:HttpService;

				for( var i:int = 0; i < length; i++ )
				{
					curService = httpServices[ i ];

					if( curService.name == service.name //
					&& curService.dataRequest == service.dataRequest //
					&& equalCallback( curService, service, "complete" ) //
					&& equalCallback( curService, service, "error" ) //
					&& equalCallback( curService, service, "progress" ) //
					&& equalCallback( curService, service, "timeOut" ))
					{
						// 若完全相等，证明是同一个服务对象，则移除，并放入对象池备用
						pushServicePool( httpServices.splice( i, 1 )[ 0 ]);
						break;
					}
				}

				if( httpServices.length == 0 )
				{
					delete _serviceCache[ service.name ];
					_numService--;
				}
			}
		}

		/**
		 * 检测指定的回调方法是否已经存在于指定的服务中
		 * @param service HTTP 服务实例
		 * @param func 待检测的方法
		 * @param type 回调方法的类型
		 * @return 若已经存在则返回 <code>true</code>，否则返回 <code>false</code>
		 */
		private function _isExistingMethod( service:HttpService, func:Function, type:String ):Boolean
		{
			var funcs:Vector.<Function> = service[ type ] as Vector.<Function>;

			if( funcs.length )
			{
				for each( var f:Function in funcs )
				{
					if( f == func )
						return true;
				}
			}

			return false;
		}

		private function isExistingMethod( service1:HttpService, service2:HttpService, type:String ):Boolean
		{
			var funcs1:Vector.<Function> = service1[ type ] as Vector.<Function>;
			var funcs2:Vector.<Function> = service2[ type ] as Vector.<Function>;

			if( funcs1.length == 0 && funcs2.length == 0 )
				return true;

			if( funcs1.length >= 0 )
			{
				for each( var f:Function in funcs2 )
				{
					if( !_isExistingMethod( service1, f, type ))
						return false;
				}

				return true;
			}

			return false;
		}

		/** 比较两个服务的某类回调方法是否完全相同  **/
		private function equalCallback( service1:HttpService, service2:HttpService, type:String ):Boolean
		{
			var funcs1:Vector.<Function> = service1[ type ] as Vector.<Function>;
			var funcs2:Vector.<Function> = service2[ type ] as Vector.<Function>;

			if( funcs1.length == funcs2.length )
			{
				var length:int = funcs1.length;

				for( var i:int = 0; i < length; i++ )
				{
					if( funcs1[ i ] != funcs2[ i ])
						return false;
				}

				return true;
			}

			return false;
		}

		/**
		 * 将 HTTP 服务实例放入对象池，待以后备用
		 * @param service HTTP 服务实例
		 */
		private function pushServicePool( service:HttpService ):void
		{
			service.release();
			HttpService.toPool( service );
		}
	}
}
